
package gamepack;

import java.awt.event.KeyEvent;
import java.awt.geom.*;
import java.awt.image.*;
import java.awt.font.*;
import java.awt.*;
import java.text.DecimalFormat;

/**
 * The Game class abstracts the real life game. A game consists of "Rooms".
   A "Room" is kind ofsubunit of a game with some intelligence.
   In many ways a "Room" is like a mini-game.

 * Room's Responsibilities:
   * Manages sprites.
   * Spawn/kill sprites within room as necessary. (apart from sprites spawning/killing each other)
   * update and render the room.
                                    */

public class Room {

static public Image defaultImg = buildRoomImage("UnInitialized Room");
public Image img;

public Viewport vp;
/*
private static long MAX_STATS_INTERVAL = 1000000000L;
    // record stats every 1 second (roughly)

private static final int NO_DELAYS_PER_YIELD = 16;

  private static int MAX_FRAME_SKIPS = 5;   // was 2;
    // no. of frames that can be skipped in any one animation loop
    // i.e the games state is updated but not rendered

  private static int NUM_FPS = 10;
     // number of FPS values stored to get an average

    private long period = 1/35 * 1000000000L;                // period between drawing in _nanosecs_


      // used for gathering statistics
  private long statsInterval = 0L;    // in ns
  private long prevStatsTime;
  private long totalElapsedTime = 0L;
  private long gameStartTime;
  private int timeSpentInGame = 0;    // in seconds

  private long frameCount = 0;
  private double fpsStore[];
  private long statsCount = 0;
  private double averageFPS = 0.0;

  private long framesSkipped = 0L;
  private long totalFramesSkipped = 0L;
  private double upsStore[];
  private double averageUPS = 0.0;

  private DecimalFormat df = new DecimalFormat("0.##");  // 2 dp
  private DecimalFormat timedf = new DecimalFormat("0.####");  // 4 dp
*/

private static Image buildRoomImage(String str) {
       Dimension d = Application.getSize();
       Image img = Application.createImage(d.width,d.height,Color.blue);
       Graphics g = img.getGraphics();
       g.setColor( Color.white );
       g.drawString( str, d.width/2-30, d.height/2 );
       return img;
}

public Room() {
       img = defaultImg;	   
       vp = new Viewport(this);
	   if ( World.currentWorld != null )
			World.currentWorld.curRoom = this; // This must happen in all Room ctrs or else the sprite' within ctr cannot see curRoom

}

public Room(String s) {
       img = buildRoomImage(s);
       vp = new Viewport(this);
	   if ( World.currentWorld != null )
		World.currentWorld.curRoom = this; // This must happen in all Room ctrs or else the sprite' within ctr cannot see curRoom   
}

public Room(Image img_,Rectangle vp_rect,int ... innerBox) {
       img = img_; //buildRoomImage(s);
       vp = new Viewport(this,vp_rect,innerBox);
	   if ( World.currentWorld != null )
	   	   World.currentWorld.curRoom = this; // This must happen in all Room ctrs or else the sprite' within ctr cannot see curRoom
}

public Room(Image img_,RoomData rd) {
		if( rd == null ) {
			throw new RuntimeException ( " RoomData is null for " + getClass().getName() + " Check if <RoomData> tag is correctly specified in the room's configuration file. " );
		}
       img = img_; //buildRoomImage(s);

	   Rectangle vp_rect = new Rectangle(rd.vx, rd.vy, rd.vw, rd.vh);
       vp = new Viewport(this,vp_rect, rd.vl, rd.vr, rd.vu, rd.vd );
	   if ( World.currentWorld != null )
	   	   World.currentWorld.curRoom = this; // This must happen in all Room ctrs or else the sprite' within ctr cannot see curRoom
}

public boolean firstTime = true;

public int getWidth() {
	return img.getWidth(null);
}

public int getHeight() {
    return img.getHeight(null);
}

public void run() {
       update();
       render( Game.gback );
       flip();
       WaveEngine.render();
       controlFrameRate();
}

// Override this method to add intelligence to the room
public void update() {
       Sprite.update();
       vp.update();
}

public void render( Graphics g) { render( g, 0,0 );  }

public void render( Graphics g,int XXX,int YYY ) {
//int XXX = 50,YYY = 50;
       int sx1 = vp.rect.x;
       int sy1 = vp.rect.y;
       int sx2 = vp.rect.x + vp.rect.width;
       int sy2 = vp.rect.y + vp.rect.height;
       int dx1=0,dy1=0;
       int dx2=dx1+vp.rect.width;
       int dy2=dy1+vp.rect.height;
       g.drawImage( img, XXX+dx1,YYY+dy1,XXX+dx2,YYY+dy2,sx1,sy1,sx2,sy2, null );
       // draw viewport for debugging..
	   if( Game.debugFlag != null && Game.debugFlag.equals("rect") ) {
			g.drawRect(XXX+vp.left,YYY+vp.up,vp.rect.width-(vp.right+vp.left),
                                vp.rect.height-(vp.down+vp.up));
		}
       // position sprites such that they are shown in viewport
       Sprite.render( g, vp, XXX, YYY);
       //Sprite.render( g, -sx1, -sy1 );
}

long beforeTime=0;
DecimalFormat myFormatter = new DecimalFormat("####");

public void controlFrameRate() {
//long fps = 35;
//long period = 1 * 1000 * 1000000L / fps;     // in nanoseconds

long afterTime, timeDiff, sleepTime;

        afterTime = Game.getRealTime();
        timeDiff = afterTime - beforeTime;
        sleepTime = ( Game.getPeriod() - timeDiff);



   //      Game.errMsg = "SleepTime = " + myFormatter.format(sleepTime / 1000000L);
   if( Application.key[KeyEvent.VK_8] ) {
       Game.debugFlag = "fps";
   }
   if( Application.key[KeyEvent.VK_9] ) {
       Game.debugFlag = "rect";
   }
   if( Application.key[KeyEvent.VK_0] ) {
       Game.debugFlag = null;
       Game.errMsg = null;
   }

    if( (Game.debugFlag != null)&& Game.debugFlag.equals("fps") ) {
        Game.errMsg = Game.getFPS() + " fps = "
        + Game.getPeriod()/1000000L + "ms = Act[" + timeDiff/1000000L
        + " ] + Sleep[" + (sleepTime/ 1000000L) + "]";
    }

        if( sleepTime > 0 ) {
          try {
               Thread.sleep( sleepTime / 1000000L );       // nano -> ms
          } catch(InterruptedException ex){}
        } else {
    //          Game.errMsg = Game.errMsg + " [SKIP]";
        }


        beforeTime = Game.getRealTime();
        Game.gameClock++;
}

Font debugMsgFont = new Font("Monospaced", Font.PLAIN, 12);

public void flip() {
    if( Game.debugFlag != null ) {
       if ( Game.errMsg != null ) {
          int msgX = 10, msgY = 10, txtHeight = 15;
            Game.gback.setColor( Color.black );
            Game.gback.fillRect( 0,0, getWidth(), txtHeight+1 );
            Game.gback.setColor( Color.red );
            Game.gback.setFont( debugMsgFont );
            Game.gback.drawString( Game.errMsg, 10, txtHeight );
       }
    }
    Game.gfront.drawImage( Game.backBuffer,0,0,null);
}

//  private void storeStats()
  /* The statistics:
       - the summed periods for all the iterations in this interval
         (period is the amount of time a single frame iteration should take),
         the actual elapsed time in this interval,
         the error between these two numbers;

       - the total frame count, which is the total number of calls to run();

       - the frames skipped in this interval, the total number of frames
         skipped. A frame skip is a game update without a corresponding render;

       - the FPS (frames/sec) and UPS (updates/sec) for this interval,
         the average FPS & UPS over the last NUM_FPSs intervals.

     The data is collected every MAX_STATS_INTERVAL  (1 sec).
  */
 /* {
    frameCount++;
    statsInterval += period;

    if (statsInterval >= MAX_STATS_INTERVAL) {     // record stats every MAX_STATS_INTERVAL
      long timeNow = TimeStamp.getTime(); //.getValue();
      timeSpentInGame = (int) ((timeNow - gameStartTime)/1000000000L);  // ns --> secs
      Game.errMsg = "Time Spent: " + timeSpentInGame + "           ";

      long realElapsedTime = timeNow - prevStatsTime;   // time since last stats collection
      totalElapsedTime += realElapsedTime;

      double timingError =
         ((double)(realElapsedTime - statsInterval) / statsInterval) * 100.0;

      totalFramesSkipped += framesSkipped;

      double actualFPS = 0;     // calculate the latest FPS and UPS
      double actualUPS = 0;
      if (totalElapsedTime > 0) {
        actualFPS = (((double)frameCount / totalElapsedTime) * 1000000000L);
        actualUPS = (((double)(frameCount + totalFramesSkipped) / totalElapsedTime)
                                                             * 1000000000L);
      }

      // store the latest FPS and UPS
      fpsStore[ (int)statsCount%NUM_FPS ] = actualFPS;
      upsStore[ (int)statsCount%NUM_FPS ] = actualUPS;
      statsCount = statsCount+1;

      double totalFPS = 0.0;     // total the stored FPSs and UPSs
      double totalUPS = 0.0;
      for (int i=0; i < NUM_FPS; i++) {
        totalFPS += fpsStore[i];
        totalUPS += upsStore[i];
      }

      if (statsCount < NUM_FPS) { // obtain the average FPS and UPS
        averageFPS = totalFPS/statsCount;
        averageUPS = totalUPS/statsCount;
      }
      else {
        averageFPS = totalFPS/NUM_FPS;
        averageUPS = totalUPS/NUM_FPS;
      }
      framesSkipped = 0;
      prevStatsTime = timeNow;
      statsInterval = 0L;   // reset
    }
  }  // end of storeStats()
   */

// Similar to destructor, override if you have
// cleanup code which needs to be done when quitting room
public void exit() { }


static Room defaultRoom = new Room("Default Room");
public static Room getDefaultRoom() { return defaultRoom; }


	public static boolean isOutside(int x,int y) {
		try {
			int w = World.currentWorld.curRoom.getWidth();
			int h = World.currentWorld.curRoom.getHeight();

			return ( x < 0 || x > w || y < 0 || y > h ) ? true : false;
		}
		catch( RuntimeException e) { return true; }
	}
}